module Visualizer::Visualisations::TreeView

import vis::Render;
import vis::Figure;
import vis::KeySym;
import Visualizer::VisualisationTreeADT;
import Number;
import String;
import List;
import IO;
import Metrics::Regular::StringUtils;

list[Figure] figureStack = [];

/*	The TreeView is the implementation of the Tree
	visualization. The visualization enables the
	user to browse through a java project and find
	hot spots in the code.
*/

/*
Example run:

import Visualizer::Visualisations::TreeView;
import Visualizer::VisualisationTreeADTBuilder;
printTree(generateVisTreeFromProject(|project://SimpleExample|));

import Visualizer::Visualisations::TreeView;
import Visualizer::BufferedTrees::SmallSQL;
printTree(getBufferedSmallSQL());
		 
import  Visualizer::Visualisations::TreeView;
import Visualizer::BufferedTrees::HSQLDB;
printTree(getBufferedHSQLDB());

import  Visualizer::Visualisations::TreeView;
import Visualizer::BufferedTrees::jmeter;
printTree(getBufferedjmeter());

writeFile(|file:///D:/evolution/export/jmeter.txt|, tree);

*/

private str shortPackageName(str name)
{
  str packageName = name;
  int nameStart = size(packageName)-30;
  if( nameStart < 0 ) { nameStart = 0; packageName = substring(packageName, nameStart); }
  else { packageName = "..." + substring(packageName, nameStart); }
  
  return packageName;
}

// generates the colors for the cyclometric complexity measure
private str ccColor(int cc) {
	if( cc <= 10 )  {
		return "Green";
	}
	else if( cc <= 20 ) {
		return "Yellow";
	}
	else if( cc <= 50 ) {
		return "Orange";
	}
	else return "Red";
}

// generates the colors for the Module size
private str moduleSizeColor(int mSize) {
	if( mSize < 250 ) {
		return "Green";
	}
	else if( mSize < 400 ) {
		return "Yellow";
	}
	else if( mSize < 700 ) {
		return "Orange";
	}
	else return "Red";
}

// Popup with package info
public FProperty packageInfo(int pkSize, int nrOfClass, int LOC, int CC){ 
  return mouseOver(
  	ellipse(
  		text(
  		    "Package size: \t\t\t" + toString(pkSize) + 
  		    "\nAmount of classes:\t\t" + toString(nrOfClass) + 
  		    "\nNCSLOC:\t\t\t" + toString(LOC) + 
  		    "\nCC:\t\t\t\t" +toString(CC) +
  		    "\nCC per 100 nNCSLOC:\t\t" + calculateCCPer100Loc(LOC, CC)
  		    ), fillColor("lightyellow"), 
            resizable(false), top(), right(), std(gap(5))
        )
    ); 
}

// Popup with top Ten Classes
public FProperty godClass(list[Class] classList)
{
  list[Figure] f = [];
  
  if (!isEmpty(classList))
  {
    for( i <- [0..(size(classList)-1)] )
    {
      f = f + [box(vcat([text(classList[i].name),text("NCSLOC: " + toString(classList[i].moduleSize), fontSize(10))]), size(200, 20), std(gap(10)), fillColor(moduleSizeColor(classList[i].moduleSize)))];
    }
  }
  
  return mouseOver(
    box(
      vcat(f), bottom()
    )
  );
} 

// Popup with top Ten CC methods
public FProperty ccMethod(list[Method] methodList)
{
  list[Figure] f = [];
  
  if (!isEmpty(methodList))
  {
    for( i <- [0..(size(methodList)-1)] )
    {
      f = f + [box(vcat([text(methodList[i].name),text("CC: " + toString(methodList[i].cc), fontSize(10))]), size(200, 20), std(gap(10)), fillColor(ccColor(methodList[i].cc)))];
    }
  }
  
  return mouseOver(
    box(
      vcat(f), bottom()
    )
  );
} 

// go back to the previous figure and render this
public FProperty back(int toStep)
{ 
  return onMouseDown(bool (int butnr, map[KeyModifier,bool] modifiers) {
    render(figureStack[toStep]);
    return true;
  });
}

// detects a click on a subpackage button
// the depth determins how deep currently are in the tree to allow us
// to use a back button
public FProperty openSubPackage(int depth, list[VisualisationTree] p, str parent)
{
  // generate the subpackage trees
  list[Figure] x = [];
  for( n <- [0..(size(p)-1)])
  {
    x = x + [getTreeFigure(p[n], depth+1, parent + ".")];
  }
  
  // render the trees in a horizontal fasion to the screen and add this figure to the 
  // figure stack
  return onMouseDown(bool (int butnr, map[KeyModifier,bool] modifiers) {
    c = false;
    Figure fig = box(
      hcat([
        box(
          text("Terug"), back(depth), left(), top(), resizable(false), size(70, 30),
          fillColor(Color () { return c ? color("GhostWhite") : color("White"); }), onMouseEnter(void () { c = true; }), onMouseExit(void () { c = false ; })
        )
        ] + x,valign(0.1))
      ,center(),fillColor("lightGray"),std(gap(10))
    ); 
    figureStack = figureStack + [fig];
    render(fig);
    return true;
  });
}

// Transforms a tree into a Figure. If the package has subpackages it will show a button
// to go to these subpackages
public Figure getTreeFigure(VisualisationTree x, int depth, str p)
{
  list[Figure] f = [];
  
  tuple[int totalModuleSize, int amountSubPackages, int amountClasses, int CC] info = calculateAgregatedMetrics(x);
  // get the top ten God Classes
  list[Class] topTenGod = getTopTenGod(x);
  // get the top then methods with highest cyclometric complexity
  list[Method] topTenCC = getTopTenCC(x);
  // if the package has subpackages shwo the subpackage button, else do not show it.
  str packageName = shortPackageName(p + x.name);
  if( size(x.packages) > 0 )
  {
      c = false; // hover is not on, is turned on at mouse over
	  return tree( 
	  				box(
	  					text("Package \n" + packageName), packageInfo(info.amountSubPackages, info.amountClasses, info.totalModuleSize, info.CC), std(gap(10))
	  				),
	  			   [
	  			   	box(
	  			   	  text("Subpackages"), 
	  			   	  openSubPackage(depth , x.packages, p + x.name), 
	  			   	  std(gap(10)),
	  			   	  fillColor(Color () { return c ? color("GhostWhite") : color("White"); }), onMouseEnter(void () { c = true; }), onMouseExit(void () { c = false ; })
	  			   	),
	  			   	box(text("Top 10 god classes"), godClass(topTenGod), std(gap(10))),
	  			   	box(text("Top 10 CC methods"), ccMethod(topTenCC), std(gap(10)))
	  			   ], std(gap(20)), manhattan(true), valign(0.1)
	  );
  }
  // return a tree without subpackage button
  return tree( box(text("Package \n" + packageName), packageInfo(info.amountSubPackages, info.amountClasses, info.totalModuleSize, info.CC), std(gap(10))),
  			   [box(text("Top 10 god classes"), godClass(topTenGod), std(gap(10))),box(text("Top 10 CC methods"), ccMethod(topTenCC), std(gap(10)))], std(gap(20)), manhattan(true), valign(0.1)
  );
}

private str calculateCCPer100Loc(int LOC, int CC){
		if (LOC <=0) return "0";//throw "LOC is not positive: <moduleSize>";
		real ccPerNLOC = CC/(LOC/100.0);
		return truncateRealToString(ccPerNLOC,2);
}

// render the first image and add it to the figure stack, to be able to do steps back
public void printTree(VisualisationTree x)
{
  // Clear the figure stack otherwise we cannot run 2 visualisations after each other.
  figureStack = [];
  // create the figure
  Figure fig = box(getTreeFigure(x, 0, ""), fillColor("lightGray"));
  // add it to the stack
  figureStack = figureStack + [fig];
  // render the image to the screen
  render(fig);
}